/* Copyright 2011-2013 Google Inc.
 * Copyright 2013 mike wakerly <opensource@hoho.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 * Project home page: https://github.com/mik3y/usb-serial-for-android
 */

package com.hoho.android.usbserial.examples;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.hrv.Algorithm.EcgJinClient;
import com.example.hrv.Algorithm.ResultOfHRV;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.IFillFormatter;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.util.HexDump;
import com.hoho.android.usbserial.util.SerialInputOutputManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Monitors a single {@link UsbSerialPort} instance, showing all data
 * received.
 *
 * @author mike wakerly (opensource@hoho.com)
 */
public class SerialConsoleActivity extends Activity {

    private static final int START_SO = 0;
    private final String TAG = "aaaaaaaaaaaaa";

    /**
     * Driver instance, passed in statically via
     * {@link #show(Context, UsbSerialPort)}.
     * <p>
     * <p/>
     * This is a devious hack; it'd be cleaner to re-create the driver using
     * arguments passed in with the {@link #startActivity(Intent)} intent. We
     * can get away with it because both activities will run in the same
     * process, and this is a simple demo.
     */
    private static UsbSerialPort sPort = null;

    private TextView mTitleTextView;
    private TextView mDumpTextView;
    private ScrollView mScrollView;
    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();

    private SerialInputOutputManager mSerialIoManager;
    private Handler mHandler = new Handler() {
    };

    private final SerialInputOutputManager.Listener mListener =
            new SerialInputOutputManager.Listener() {

                @Override
                public void onRunError(Exception e) {
                    Log.d(TAG, "Runner stopped.");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mDumpTextView.setText("error:" + e.getMessage());
                        }
                    });
                }

                @Override
                public void onNewData(final byte[] data) {
                    SerialConsoleActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            SerialConsoleActivity.this.updateReceivedData(data);
                        }
                    });
                }
            };
    private int getValidDataHeadFlag = 0;
    private int getValidDataLen = 0;
    private int hrvValue = 0;
    private LinkedList<Long> pulseWaves = new LinkedList<Long>();
    private int pulseWaveSize = 3000;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

        }
    };
    private boolean checkResultState = true;
    private int sclValue = 0;
    private int j = 0;
    private LineChart mChartHrv;
    private List RRList = new ArrayList<>();
    private int hrvResultFlag = 0;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.serial_console);
        mTitleTextView = (TextView) findViewById(R.id.demoTitle);
        mDumpTextView = (TextView) findViewById(R.id.consoleText);
        mScrollView = (ScrollView) findViewById(R.id.demoScroller);

        mChartHrv = (LineChart) findViewById(R.id.lineChartHRV);
        try {
            sPort.setDTR(true);
            sPort.setRTS(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        initSo();
    }

    private void initSo() {
        EcgJinClient.InitButtFiler(500, EcgJinClient.kLoPass, 2, 3.0f, 0.0f);
    }


    @Override
    protected void onPause() {
        super.onPause();
        stopIoManager();
        if (sPort != null) {
            try {
                sPort.close();
            } catch (IOException e) {
                // Ignore.
            }
            sPort = null;
        }
        finish();
    }

    void showStatus(TextView theTextView, String theLabel, boolean theValue) {
        String msg = theLabel + ": " + (theValue ? "enabled" : "disabled") + "\n";
        theTextView.append(msg);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "Resumed, port=" + sPort);
        if (sPort == null) {
            mTitleTextView.setText("No serial device.");
        } else {
            final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

            UsbDeviceConnection connection = usbManager.openDevice(sPort.getDriver().getDevice());
            if (connection == null) {
                mTitleTextView.setText("Opening device failed");
                return;
            }

            try {
                sPort.open(connection);
                sPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);

                showStatus(mDumpTextView, "CD  - Carrier Detect", sPort.getCD());
                showStatus(mDumpTextView, "CTS - Clear To Send", sPort.getCTS());
                showStatus(mDumpTextView, "DSR - Data Set Ready", sPort.getDSR());
                showStatus(mDumpTextView, "DTR - Data Terminal Ready", sPort.getDTR());
                showStatus(mDumpTextView, "DSR - Data Set Ready", sPort.getDSR());
                showStatus(mDumpTextView, "RI  - Ring Indicator", sPort.getRI());
                showStatus(mDumpTextView, "RTS - Request To Send", sPort.getRTS());
            } catch (IOException e) {
                Log.e(TAG, "Error setting up device: " + e.getMessage(), e);
                mTitleTextView.setText("Error opening device: " + e.getMessage());
                try {
                    sPort.close();
                } catch (IOException e2) {
                    // Ignore.
                }
                sPort = null;
                return;
            }
            mTitleTextView.setText("Serial device: " + sPort.getClass().getSimpleName());
        }
        onDeviceStateChange();
    }

    private void stopIoManager() {
        if (mSerialIoManager != null) {
            Log.i(TAG, "Stopping io manager ..");
            mSerialIoManager.stop();
            mSerialIoManager = null;
        }
    }

    private void startIoManager() {
        if (sPort != null) {
            Log.i(TAG, "Starting io manager ..");
            mSerialIoManager = new SerialInputOutputManager(sPort, mListener);
            mExecutor.submit(mSerialIoManager);
        }
    }

    private void onDeviceStateChange() {
        stopIoManager();
        startIoManager();
    }

    private void updateReceivedData(byte[] paramByte) {
        final String message = "Read " + paramByte.length + " bytes: \n"
                + HexDump.dumpHexString(paramByte) + "\n\n";
        Log.e(TAG, "原始数据: " + message);
        calcute(paramByte);
    }

    private String IntToHex2(int paramInt) {
        char[] arrayOfChar = new char[2];
        arrayOfChar[0] = Character.forDigit(0xF & paramInt >> 4, 16);
        arrayOfChar[1] = Character.forDigit(paramInt & 0xF, 16);
        return new String(arrayOfChar);
    }

    private void calcute(byte[] paramByte) {
        for (int i = 0; i < paramByte.length; i++) {
            if (paramByte[i] == (byte) 0xEE || paramByte[i] == (byte) 0xEF) {
                if (paramByte[i] == (byte) 0xEE) {
                    getValidDataHeadFlag = 1; // 脉搏波
                } else {
                    getValidDataHeadFlag = 0;// scl
                }
                getValidDataLen = 0;
            } else if (paramByte[i] == (byte) 0xFF) {
                if (getValidDataLen == 2 && 1 == getValidDataHeadFlag) {
                    hrvValue = 1024 - hrvValue;
                    j++;
                    Log.e(TAG, "calcute: hrvValue;" + hrvValue + "---shuju;" + j);
                    mDumpTextView.append("calcute: hrvValue;" + hrvValue + "---当前条数;" + j + "\n");
                    mScrollView.smoothScrollTo(0, mDumpTextView.getBottom());
//                    actualHRVData(hrvValue);

                    if (pulseWaves != null) {
                        if (pulseWaves.size() > pulseWaveSize) {
                            Log.e(TAG, "updateReceivedData: " + pulseWaveSize);
                            startCalcute(paramByte, pulseWaves);
                            checkResultState = false;
                            pulseWaveSize += 3000;
                        } else {
                            checkResultState = true;
                            pulseWaves.add((long) hrvValue);
                        }
                    }
                } else if (getValidDataLen == 2 && 0 == getValidDataHeadFlag) {
                    sclValue = hrvValue;
                }
            } else {
                getValidDataLen++;
//                mDumpTextView.append("getValidDataLen:" + getValidDataLen + "\n");
                if (getValidDataLen == 1) {
//                    mDumpTextView.append("getValidDataLen == 1:" + "\n");
                    hrvValue = (0x0FF & paramByte[i]) << 2;
                } else {
//                    mDumpTextView.append("getValidDataLen == 1:else" + "\n");
                    hrvValue = hrvValue + ((0x0c0 & paramByte[i]) >> 6);
                }
            }
        }

    }

    /**
     * 算法集合长度大于3000，获取心率
     *
     * @param paramByte
     * @param pulseWaves
     */
    private void startCalcute(byte[] paramByte, LinkedList<Long> pulseWaves) {
        int[] pulseWave = new int[3000];
        for (int i = 0; i < 3000; i++) {
            pulseWave[i] = pulseWaves.get(i).intValue();
        }
        // 得到RR间期长度
        int mRRInterval = EcgJinClient.CaculateRRInterval(pulseWave, pulseWave.length);
        mDumpTextView.append(" mRRInterval-----:" + mRRInterval + "\n");
        mScrollView.smoothScrollTo(0, mDumpTextView.getBottom());
        int[] mRRIntervalArr = new int[mRRInterval];
        for (int i = 0; i < mRRInterval; i++) {
            mRRIntervalArr[i] = pulseWave[i];
            Log.e(TAG, "mRRIntervalArr: " + pulseWave[i]);
        }

        if (mRRIntervalArr.length < 5) {
            return;
        }
        for (int i = 0; i < mRRIntervalArr.length; i++) {
            RRList.add(Integer.valueOf(2 * mRRIntervalArr[i]));
        }
        if (mRRIntervalArr.length > 0) {
            double mXinlv = HRComp(mRRIntervalArr, 500);
            Log.e(TAG, "startCalcute: mXinlv" + mXinlv);
            mDumpTextView.append(" mXinlv-----:" + mXinlv + "\n");
            mScrollView.smoothScrollTo(0, mDumpTextView.getBottom());
        }
        if (hrvResultFlag > 2 && RRList.size() > 5) {
            getHrvResult();
        }
        hrvResultFlag = 1 + hrvResultFlag;



        ResultOfHRV resultOfHRV = new ResultOfHRV();
        ResultOfHRV localResultOfHRV2 = null;
        try {
            localResultOfHRV2 = EcgJinClient.CalculateHRVResultByRRData(mRRIntervalArr, mRRIntervalArr.length, 1000, resultOfHRV);
            if (localResultOfHRV2.BRPA < 0.0f) {
                Log.e(TAG, "resultOfHRV.BRPA" + localResultOfHRV2.BRPA + "");
                localResultOfHRV2.BRPA = 0.0f;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String str;
        if (localResultOfHRV2 != null) {
            str = "resultOfHRV is null ";
            Log.e(TAG, "ResultOfHRV" + str);
        }
    }

    private void getHrvResult() {

    }

//    private void actualHRVData(int hrvValue) {
////        mDumpTextView.append("hrvValue:" + hrvValue + "\n");
//        mDumpTextView.setTextColor(Color.BLUE);
//        mScrollView.smoothScrollTo(0, mDumpTextView.getBottom());
//        initChart(mChartHrv, hrvValue);
//    }

   /* private void initChart(LineChart chart, int hrvValue) {
        setData(chart, hrvValue);
        chart.setViewPortOffsets(0, 0, 0, 0);
        chart.setBackgroundColor(Color.rgb(62, 62, 126));

        // no description text
        chart.getDescription().setEnabled(false);

        // enable touch gestures
        chart.setTouchEnabled(false);

        // enable scaling and dragging
        chart.setDragEnabled(false);
        chart.setScaleEnabled(false);

        // if disabled, scaling can be done on x- and y-axis separately
        chart.setPinchZoom(false);

        chart.setDrawGridBackground(false);
        chart.setMaxHighlightDistance(300);

        XAxis x = chart.getXAxis();
        x.setEnabled(false);

        YAxis y = chart.getAxisLeft();
        y.setLabelCount(6, false);
        y.setTextColor(Color.WHITE);
        y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
        y.setDrawGridLines(false);
        y.setAxisLineColor(Color.WHITE);

        chart.getAxisRight().setEnabled(false);
        chart.getLegend().setEnabled(false);
        chart.animateXY(2000, 2000);

        // don't forget to refresh the drawing
        chart.invalidate();
    }*/

    /*private void setData(LineChart chart, int hrvValue) {
        ArrayList<Entry> values = new ArrayList<>();
        for (int i = 0; i < count; i++) {
//            float val = (float) (Math.random() * (range + 1)) + 20;

            values.add(new Entry(i, val));
        }
        LineDataSet set1;
        if (chart.getData() != null &&
                chart.getData().getDataSetCount() > 0) {
            set1 = (LineDataSet) mChartHrv.getData().getDataSetByIndex(0);
            set1.setValues(values);
            chart.getData().notifyDataChanged();
            chart.notifyDataSetChanged();
        } else {
            // create a dataset and give it a type
            set1 = new LineDataSet(values, "DataSet 1");
            set1.setMode(LineDataSet.Mode.CUBIC_BEZIER);
            set1.setCubicIntensity(0.2f);
            set1.setDrawFilled(true);
            set1.setDrawCircles(false);
            set1.setLineWidth(1.8f);
            set1.setCircleRadius(4f);
            set1.setCircleColor(Color.WHITE);
            set1.setHighLightColor(Color.rgb(244, 117, 117));
            set1.setColor(Color.WHITE);
            set1.setFillColor(Color.WHITE);
            set1.setFillAlpha(100);
            set1.setDrawHorizontalHighlightIndicator(false);
            set1.setFillFormatter(new IFillFormatter() {
                @Override
                public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
                    return chart.getAxisLeft().getAxisMinimum();
                }
            });
            // create a data object with the data sets
            LineData data = new LineData(set1);
            data.setValueTextSize(9f);
            data.setDrawValues(false);
            chart.setData(data);
        }
    }*/

    /**
     * 根据RR间期计算心率
     *
     * @param RR     RR间期
     * @param sample 采样率
     * @return
     */
    public double HRComp(int[] RR, int sample) {
        if (RR.length == 0) {
            return 0;
        }
        int len = RR.length;
        int sum = 0;
        for (int i = 0; i < len; i++) {
            sum += RR[i];
        }
        double RRMean = sum / len;
        return sample / RRMean * 60;
    }

    /**
     * Starts the activity, using the supplied driver instance.
     *
     * @param context
     * @param port
     */
    static void show(Context context, UsbSerialPort port) {
        sPort = port;
        final Intent intent = new Intent(context, SerialConsoleActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
        context.startActivity(intent);
    }

}
